﻿#if !NET45
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
#else
using System.Web;
#endif

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;


namespace JESAI.HtmlTemplate.Pdf.MemoryCache
{
    public class MemoryCacheService: ICacheService
    {
#if !NET45
        protected IMemoryCache _cache;
        public MemoryCacheService(IMemoryCache cache)
        {
            _cache = cache;
        }
        public MemoryCacheService()
        {
            _cache = new Microsoft.Extensions.Caching.Memory.MemoryCache(Options.Create(new MemoryCacheOptions()));
        }
#else
        protected ICacheManager _cache;
        public MemoryCacheService()
        {
            _cache = new HttpRuntimeCacheManager();
        }
#endif






        /// <summary>
        /// 验证缓存项是否存在
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <returns></returns>
        public bool Exists(string key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            object cached;
#if !NET45
            return _cache.TryGetValue(key,out cached);
#else
            return _cache.Exists(key);
#endif
        }

        /// <summary>
        /// 添加缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="value">缓存Value</param>
        /// <returns></returns>
        public bool Add(string key, object value)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }
            if (!Exists(key))
#if !NET45
            _cache.Set(key, value);
#else
             _cache.Set(key,value);
#endif
            return Exists(key);
        }
         /// <summary>
         /// 添加缓存
         /// </summary>
         /// <param name="key">缓存Key</param>
         /// <param name="value">缓存Value</param>
         /// <param name="expiresSliding">滑动过期时长（如果在过期时间内有操作，则以当前时间点延长过期时间）</param>
         /// <param name="expiressAbsoulte">绝对过期时长</param>
         /// <returns></returns>
         public bool Add(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
         {
             if (key == null)
             {
                 throw new ArgumentNullException(nameof(key));
             }
             if (value == null)
             {
                 throw new ArgumentNullException(nameof(value));
             }
            if (!Exists(key))
#if !NET45
                _cache.Set(key, value,
                     new MemoryCacheEntryOptions()
                     .SetSlidingExpiration(expiresSliding)
                     .SetAbsoluteExpiration(expiressAbsoulte)
                     );
#else
                _cache.Set(key, value, expiresSliding, expiressAbsoulte);
#endif
            return Exists(key);
         }
         /// <summary>
         /// 添加缓存
         /// </summary>
         /// <param name="key">缓存Key</param>
         /// <param name="value">缓存Value</param>
         /// <param name="expiresIn">缓存时长</param>
         /// <param name="isSliding">是否滑动过期（如果在过期时间内有操作，则以当前时间点延长过期时间）</param>
         /// <returns></returns>
         public bool Add(string key, object value, TimeSpan expiresIn, bool isSliding = false)
         {
             if (key == null)
             {
                 throw new ArgumentNullException(nameof(key));
             }
             if (value == null)
             {
                 throw new ArgumentNullException(nameof(value));
             }
            if (!Exists(key))
#if !NET45
                if (isSliding)
             _cache.Set(key, value,
                 new MemoryCacheEntryOptions()
                 .SetSlidingExpiration(expiresIn)
                 );
             else
                 _cache.Set(key, value,
                 new MemoryCacheEntryOptions()
                 .SetAbsoluteExpiration(expiresIn)
                 );
#else
                _cache.Set(key, value, expiresIn, isSliding);
#endif

            return Exists(key);
        }
        /// <summary>
        /// 删除缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <returns></returns>
        public bool Remove(string key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            _cache.Remove(key);
 
            return !Exists(key);
        }
        /// <summary>
        /// 批量删除缓存
        /// </summary>
        /// <param name="key">缓存Key集合</param>
        /// <returns></returns>
        public void RemoveAll(IEnumerable<string> keys)
        {
            if (keys == null)
            {
                throw new ArgumentNullException(nameof(keys));
            }
 
            keys.ToList().ForEach(item => _cache.Remove(item));
        }
        /// <summary>
          /// 获取缓存
          /// </summary>
          /// <param name="key">缓存Key</param>
          /// <returns></returns>
          public T Get<T>(string key) where T : class
          {
             if (key == null)
             {
                 throw new ArgumentNullException(nameof(key));
             }
             return _cache.Get(key) as T;
         }
         /// <summary>
         /// 获取缓存
         /// </summary>
         /// <param name="key">缓存Key</param>
         /// <returns></returns>
         public object Get(string key)
         {
             if (key == null)
             {
                 throw new ArgumentNullException(nameof(key));
             }
             return _cache.Get(key);
         }
         /// <summary>
         /// 获取缓存集合
         /// </summary>
         /// <param name="keys">缓存Key集合</param>
         /// <returns></returns>
         public IDictionary<string, object> GetAll(IEnumerable<string> keys)
         {
             if (keys == null)
             {
                 throw new ArgumentNullException(nameof(keys));
             }
 
             var dict = new Dictionary<string, object>();
 
             keys.ToList().ForEach(item => dict.Add(item, _cache.Get(item)));
 
             return dict;
        }
        /// <summary>
        /// 修改缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="value">新的缓存Value</param>
        /// <returns></returns>
        public bool Replace(string key, object value)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }
            if (Exists(key))
                if (!Remove(key)) return false;
 
            return Add(key, value);
 
        }
        /// <summary>
        /// 修改缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="value">新的缓存Value</param>
        /// <param name="expiresSliding">滑动过期时长（如果在过期时间内有操作，则以当前时间点延长过期时间）</param>
        /// <param name="expiressAbsoulte">绝对过期时长</param>
        /// <returns></returns>
        public bool Replace(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }
            if (Exists(key))
                if (!Remove(key)) return false;
 
            return Add(key, value, expiresSliding, expiressAbsoulte);
        }
        /// <summary>
        /// 修改缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="value">新的缓存Value</param>
        /// <param name="expiresIn">缓存时长</param>
        /// <param name="isSliding">是否滑动过期（如果在过期时间内有操作，则以当前时间点延长过期时间）</param>
        /// <returns></returns>
        public bool Replace(string key, object value, TimeSpan expiresIn, bool isSliding = false)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }
            if (Exists(key))
                if (!Remove(key)) return false;
 
            return Add(key, value, expiresIn, isSliding);
        }
        public void Dispose()
        {
            if (_cache != null)
#if !NET45
                _cache.Dispose();
#endif
            GC.SuppressFinalize(this);
        }

        public async Task<bool> AddAsync(string key, object value)
        {            
            return this.Add(key, value); 
        }

        public async Task<bool> AddAsync(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
        {
            return this.Add(key, value, expiresSliding,expiressAbsoulte);
        }

        public async Task<bool> AddAsync(string key, object value, TimeSpan expiresIn, bool isSliding = false)
        {
            return this.Add(key, value, expiresIn, isSliding);
        }

        public async Task<bool> RemoveAsync(string key)
        {
            return this.Remove(key);
        }

        public async Task RemoveAllAsync(IEnumerable<string> keys)
        {
            foreach(var key in keys)
            {
                this.Remove(key);
            }
        }

        public async Task<T> GetAsync<T>(string key) where T : class
        {
            return this.Get<T>(key);
        }

        public async Task<object> GetAsync(string key)
        {
            return this.Get(key);
        }

        public async Task<IDictionary<string, object>> GetAllAsync(IEnumerable<string> keys)
        {
            return this.GetAll(keys);
        }

        public async Task<bool> ReplaceAsync(string key, object value)
        {
            return this.Replace(key, value);
        }

        public async Task<bool> ReplaceAsync(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
        {
            return this.Replace(key, value, expiresSliding, expiressAbsoulte);
        }

        public async Task<bool> ReplaceAsync(string key, object value, TimeSpan expiresIn, bool isSliding = false)
        {
            return this.Replace(key, value, expiresIn, isSliding);
        }

        public bool Add<T>(string key, T value) where T : class
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }
            if (!Exists(key))
#if !NET45
                _cache.Set<T>(key, value);
#else
                _cache.Set(key, value);
#endif
            return Exists(key);
        }
        public bool Add<T>(string key, T value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte) where T : class
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }
            if (!Exists(key))
#if !NET45
                _cache.Set<T>(key, value,
                     new MemoryCacheEntryOptions()
                     .SetSlidingExpiration(expiresSliding)
                     .SetAbsoluteExpiration(expiressAbsoulte)
                     );
#else
                _cache.Set(key,value, expiressAbsoulte, expiresSliding);
#endif
            return Exists(key);
        }
        public async Task<bool> AddAsync<T>(string key, T value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte) where T : class
        {
           
            return this.Add<T>(key,value,expiresSliding,expiressAbsoulte);
        }
        public async Task<bool> AddAsync<T>(string key, T value) where T : class
        {          
            return this.Add<T>(key,value);
        }

        //public async Task<bool> AddAsync<T>(string key, T value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte) where T : class
        //{
        //    throw new NotImplementedException();
        //}

        public bool Add<T>(string key, T value, TimeSpan expiresIn, bool isSliding = false)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }
            if (!Exists(key))
#if !NET45
                if (isSliding)
                    _cache.Set<T>(key, value,
                        new MemoryCacheEntryOptions()
                        .SetSlidingExpiration(expiresIn)
                        );
                else
                    _cache.Set<T>(key, value,
                    new MemoryCacheEntryOptions()
                    .SetAbsoluteExpiration(expiresIn)
                    );
#else
                _cache.Set(key, value, expiresIn, isSliding);
#endif
            return Exists(key);
        }

        public async Task<bool> AddAsync<T>(string key, T value, TimeSpan expiresIn, bool isSliding = false)
        {
            return Add<T>(key, value, expiresIn, isSliding);
        }
    }
}
